package main 

import (
	"fmt"
	"net"
	"encoding/binary"
	"time"
	"bufio"
)

const (
	RoleConnState_None	= 0
	RoleConnState_New	= 1		// 新连接
	RoleConnState_Close	= 2		// 连接关闭
	RoleConnState_Normal= 3		// 正常
)

type RoleConn struct {
	conn net.Conn
	
	readPktChan chan *Packet
	WritePktChan chan *Packet 
}

func NewRoleConn(conn net.Conn, pktc chan *Packet) *RoleConn {
	rc := &RoleConn{}
	rc.conn = conn
	
	rc.readPktChan = pktc
	rc.WritePktChan = make(chan *Packet, 100)
	
	fmt.Println("new roleconn:", conn)
	
	return rc
}

type Packet struct {
	connState int  // 见RoleConnState_X
	Err error
	
	RConn *RoleConn
	
	MsgType int
	Data []byte    // logic是排除4字节头部的数据, net写入时是序列化后所有数据
}

func (pkt *Packet)String() (str string) {
	str = fmt.Sprintf("connState:%d, err:%v, msgType:%v, data:%s\n", pkt.connState, pkt.Err, pkt.MsgType, string(pkt.Data))
	return
}

func NewPacket(state int, err error, rc *RoleConn, msg int, data []byte) *Packet {
	pkt := &Packet{}
	pkt.connState = state
	pkt.RConn = rc
	pkt.MsgType = msg
	pkt.Data = data
	pkt.Err = err
	
	return pkt
}

func main() {
	ln, err := net.Listen("tcp", ":8080")
	if err != nil {
		fmt.Println("net.Dial fail,", err)
		return
	}
	
	// net->logic chain
	readPktChan := make(chan *Packet, 1024)
	
	// 逻辑主go程
	go handleLogic(readPktChan)
	
	// 主go程监听网络连接
	for {
		conn, err := ln.Accept()
		if err != nil {
			fmt.Println("ln accept fail.", err)
			continue
		}

		rc := NewRoleConn(conn, readPktChan)
		go handleRead(rc)
		go handleWrite(rc)
	}
}


func handleRead(rc *RoleConn) {
	defer fmt.Println("conn handleRead over.", rc.conn)
	//defer rc.conn.Close()
	defer close(rc.WritePktChan)
	br := bufio.NewReader(rc.conn)
	headSize := 0
	headArray := [4]byte{}
	pktSize := 0
	var pktSlice []byte
	for {
		// read head
		for headSize < len(headArray) {
			rn, err := br.Read(headArray[headSize:])
			if err != nil {
				//fmt.Println("conn read err:", err)
				pkt := NewPacket(RoleConnState_Close, err, rc, 0, nil)
				rc.readPktChan <- pkt
				return
			}
			headSize = headSize + rn
		}
		
		//fmt.Println("read head size:", headSize)
		
		pktLen := binary.BigEndian.Uint16(headArray[0:2]) - 2
		//fmt.Println("read head pkt len:", pktLen)
		
		if pktSlice == nil {
			pktSlice = make([]byte, pktLen)
			pktSize = 0
			//fmt.Println("conn read alloc pkt slice:", pktLen)
		}
		
		// read pkt
		//fmt.Println("conn read pkt", pktSize, len(pktSlice))
		rn, err := br.Read(pktSlice[pktSize:])
		if err != nil {
			//fmt.Println("conn read err:", err)
			pkt := NewPacket(RoleConnState_Close, err, rc, 0, nil)
			rc.readPktChan <- pkt
			return
		}
		
		// check full pkt
		//fmt.Println("conn read pkt data", rn)
		pktSize = pktSize + rn
		if pktSize == int(pktLen) {
			msgType := binary.BigEndian.Uint16(headArray[2:4])
			pkt := NewPacket(RoleConnState_Normal, nil, rc, int(msgType), pktSlice)
			rc.readPktChan <- pkt
			
			//fmt.Println("conn read one pkt.", pktLen, msgType)
			
			headSize = 0
			pktSize = 0
			pktSlice = nil
		}
	}
}

func handleWrite(rc *RoleConn) {
	defer fmt.Println("conn handleWrite over.", rc.conn)
	//defer rc.conn.Close()
	// 发送消息
	for {
		pkt, ok := <- rc.WritePktChan
		if !ok {
			//fmt.Println("conn writechan closed.")
			return
		} 
		
		// 发送数据
		buff := pkt.Data[:]
		wsize := len(buff)
		wn, err := rc.conn.Write(buff)
		if err != nil {
			//fmt.Println("conn write failed,", err)
			pkt := NewPacket(RoleConnState_Close, err, rc, 0, nil)
			rc.readPktChan <- pkt
			return
		}
		for wn < wsize {
			wt, err := rc.conn.Write(buff[wn:])
			if err != nil {
				//fmt.Println("conn write failed,", err)
				pkt := NewPacket(RoleConnState_Close, err, rc, 0, nil)
				rc.readPktChan <- pkt
				return //err
			}
			wn += wt
		}
	}
}

func handleLogic(c chan *Packet) {
	tick := time.Tick(100 * time.Millisecond)
	for {
		select {
		case pkt := <- c:
			dispatchPacket(pkt)
		case now := <- tick:
			tickLogic(now)
		}
	}
}

func dispatchPacket(pkt *Packet) {
	//fmt.Println("logic dispatch pkt:", pkt)
	//data := [40*1024]byte{}
	if pkt.connState == RoleConnState_Normal {
		//binary.BigEndian.PutUint16(data[0:2], uint16(len(pkt.Data) + 2))
		//binary.BigEndian.PutUint16(data[2:4], uint16(len(pkt.Data) + 2))
		select {
		case pkt.RConn.WritePktChan <- pkt:
		default:
		}
	}
}

func tickLogic(now time.Time) {
	//fmt.Println("tick ...", now)
}